package com.tools.web.interceptor.limit;

import com.tools.common.object.Note;
import com.tools.common.thread.Timeunit;
import com.tools.web.interceptor.auth.AuthenticationManager;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * 使用 RedisTemplate 客户端操作 Redis 实现的 IP-URI 同 IP 请求同一个接口的请求限流拦截器
 * */
@Note("使用 RedisTemplate 客户端操作 Redis 实现的 IP-URI 同 IP 请求同一个接口的请求限流拦截器")
public final class RedisTemplateRequestLimitInterceptor extends AbsRequestLimitInterceptor implements RequestLimitRedisLuaScript {

    @Note("操作 Redis 的客户端")
    private final RedisTemplate<String, String> redisTemplate;

    public RedisTemplateRequestLimitInterceptor(RedisTemplate<String, String> redisTemplate, RequestLimitSource source) {
        this(redisTemplate, source, null);
    }

    public RedisTemplateRequestLimitInterceptor(RedisTemplate<String, String> redisTemplate, RequestLimitSource source, AuthenticationManager authenticationManager) {
        super(source, authenticationManager);
        this.redisTemplate = Objects.requireNonNull(redisTemplate, "操作 Redis 集群的客户端 RedisTemplate<String, String> 不能为 null");
    }


    @Override
    protected boolean protected_checkIsAllow(RequestLimit limit, HttpServletRequest request, HttpServletResponse response,
                                             AuthenticationManager authenticationManager) throws Exception {
        String key = super.protected_getKey(request);
        long time = limit.time();
        Timeunit timeunit = limit.timeunit();
        long expiration = Timeunit.toSecond(time, timeunit);
        Integer result = this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                CHECK_LIMIT.getBytes(),
                ReturnType.INTEGER,
                1,
                key.getBytes(),
                String.valueOf(expiration).getBytes(),
                String.valueOf(limit.allow()).getBytes()
        ));
        if(result != null && result == 1) return true;
        response.getWriter().write(super.getLimitErrorMessage());
        if(authenticationManager != null) {
            authenticationManager.handleDisable(request);
        }
        return false;
    }
}
